using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Web;


namespace DeathByCaptcha
{
    internal class ApiCaller
    {
        public const string Version = "2.2";
        public const string UserAgent = "Death by Captcha/.NET";
        public const string ServerUrl = "http://www.deathbycaptcha.com/api";
        public const string ResponseContentType = "text/plain";
        public const int MinPollInterval = 10;


        private int pollInterval = 10;
        public int PollInterval
        {
            get
            {
                return pollInterval;
            }
            set
            {
                if (ApiCaller.MinPollInterval > value) {
                    throw new ArgumentOutOfRangeException(
                        "Poll interval can't be less than " + ApiCaller.MinPollInterval + " sec"
                    );
                }
                pollInterval = value;
            }
        }

        private Hashtable response;
        public Hashtable Response
        {
            get
            {
                return response;
            }
            set
            {
                throw new AccessViolationException(
                    "Last API response is read-only"
                );
            }
        }

        public string Error
        {
            get
            {
                return (response.ContainsKey("error") && "0" != (string)response["error"])
                    ? (string)response["error"]
                    : "";
            }
            set
            {
                throw new AccessViolationException(
                    "Last response error code is read-only"
                );
            }
        }

        public WebProxy Proxy;


        public ApiCaller()
        {
            this.Init();
        }

        public ApiCaller Init()
        {
            this.response = new Hashtable();
            return this;
        }

        public bool Call(string method, byte[] data, string contentType)
        {
            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(ApiCaller.ServerUrl + "/" + method);
                req.Accept = ApiCaller.ResponseContentType;
                req.UserAgent = ApiCaller.UserAgent + "; v" + ApiCaller.Version;

                if (null != this.Proxy)
                {
                    req.Proxy = this.Proxy;
                }

                if (0 < data.Length)
                {
                    req.Method = "POST";
                    if ("" != contentType)
                    {
                        req.ContentType = contentType;
                    }
                    req.ContentLength = data.Length;

                    Stream rs = req.GetRequestStream();
                    rs.Write(data, 0, data.Length);
                    rs.Close();
                }
                else
                {
                    req.Method = "GET";
                }

                this.response.Clear();
                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                StreamReader reader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8);
                string respRaw = reader.ReadToEnd();
                if ("" != respRaw)
                {
                    string[] kv = new string[2];
                    foreach (string s in respRaw.Split(new Char[1] { '&' }))
                    {
                        kv = s.Split(new Char[] { '=' }, 2);
                        this.response.Add(HttpUtility.UrlDecode(kv[0]), HttpUtility.UrlDecode(kv[1]));
                    }
                }
                reader.Close();
                bool result =
                    (HttpStatusCode.OK == resp.StatusCode) ||
                    (HttpStatusCode.SeeOther == resp.StatusCode);
                resp.Close();
                return result;
            }
            catch
            {
                return false;
            }
        }

        public bool Call(string method, Hashtable data)
        {
            string[] entries = new string[data.Count];

            int i = 0;
            foreach (DictionaryEntry entry in data) {
                entries[i] = entry.Key + "=" + entry.Value;
                i++;
            }

            return this.Call(method, Encoding.ASCII.GetBytes(String.Join("&", entries)), "application/x-www-form-urlencoded");
        }

        public bool Call(string method, byte[] data)
        {
            return this.Call(method, data, "");
        }

        public bool Call(string method, string data)
        {
            return this.Call(method, Encoding.ASCII.GetBytes(data), "");
        }

        public bool Call(string method)
        {
            return this.Call(method, new byte[] {}, "");
        }
    }
}
